using System.Text.RegularExpressions;
using Aurora.Redis.Tools;
using Microsoft.Extensions.Caching.Distributed;
using NewLife.Caching;

namespace Aurora.Redis.NewLifeRedis;

public class NewLifeRedisCache : IRedisCache, IDistributedCache
{
    private readonly NewLifeRedisFactory _factory;

    public NewLifeRedisCache(NewLifeRedisFactory factory)
    {
        _factory = factory;
    }

    private T Exec<T>(Func<FullRedis, T> func)
    {
        var redis = _factory.Get();
        return func(redis);
    }

    private Task<T> ExecAsync<T>(Func<FullRedis, T> func)
    {
        var redis = _factory.Get();
        return Task.FromResult(func(redis));
    }
    public void Dispose()
    {
        //不处理
    }

    public bool Add(string key, string value, TimeSpan timeSpan)
    {
        return Exec(redis => redis.Add(key, value, timeSpan.TotalSeconds.ToInt()));
    }

    public bool Add<T>(string key, T value, TimeSpan timeSpan)
    {
        return Exec(redis => redis.Add(key, value, timeSpan.TotalSeconds.ToInt()));
    }

    public T LPop<T>(string key)
    {
        return Exec(redis => redis.LPOP<T>(key));
    }

    public T Get<T>(string key)
    {
        return Exec(redis => redis.Get<T>(key));
    }

    public long Del(string key)
    {
        return Exec(redis => redis.Remove(key));
    }

    public Task<long> DelByPatternAsync(string pattern)
    {
        if (string.IsNullOrEmpty(pattern))
            return Task.FromResult(0L);
        pattern = CacheTool.PatternRegexReplace(pattern);
        var keys = Exec(redis =>
        {
            var values = redis.Search(pattern, int.MaxValue);
            return values?.ToList();
        });
        if (keys != null && keys.Any())
        {
            return Task.FromResult((long)(Exec(redis => redis.Remove(keys.ToArray()))));
        }

        return default;
    }

    public Task<List<string>> GetKeys(string pattern)
    {
        if (string.IsNullOrEmpty(pattern))
            return Task.FromResult(new List<string>());
        pattern = CacheTool.PatternRegexReplace(pattern);
        var keys = Exec(redis =>
        {
            return redis.Search(pattern, int.MaxValue);
        });
        return Task.FromResult(keys?.ToList());
    }

    public Task<bool> AddAsync(string key, string value, TimeSpan timeSpan)
    {
        return Task.FromResult(Add(key, value, timeSpan));
    }

    public Task<bool> AddAsync<T>(string key, T value, TimeSpan timeSpan)
    {
        return Task.FromResult(Add(key, value, timeSpan));
    }

    public Task<T> LPopAsync<T>(string key)
    {
        return Task.FromResult(LPop<T>(key));
    }

    public Task<T> GetAsync<T>(string key)
    {
        return Task.FromResult(Get<T>(key));
    }

    public Task<long> DelAsync(string key)
    {
        return Task.FromResult(Del(key));
    }

    public Task IncrementAsync(string key)
    {
        Exec(cache =>
        {
            return cache.Increment(key, 1);
        });
        return Task.CompletedTask;
    }

    public Task IncrementAsync(string key, int value = 1, TimeSpan? ttl = null)
    {
        Exec(cache =>
        {
            var data = cache.Increment(key, value);
            if (ttl.HasValue)
            {
                cache.SetExpire(key, ttl.Value);
            }
            return data;
        });
        return Task.CompletedTask;
    }



    public byte[] Get(string key)
    {
        return Exec(cache => cache.Get<byte[]>(key));
    }

    public Task<byte[]> GetAsync(string key, CancellationToken token = default)
    {
        return ExecAsync(cache => cache.Get<byte[]>(key));
    }

    public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
    {
        TimeSpan span = TimeSpan.Zero;
        if (options.AbsoluteExpiration != null)
        {
            span = options.AbsoluteExpiration.Value - DateTime.UtcNow;
        }
        else if (options.AbsoluteExpirationRelativeToNow != null)
        {
            span = options.AbsoluteExpirationRelativeToNow.Value;
        }
        Exec(cache => cache.Set(key, value, span == TimeSpan.Zero ? -1 : span.Seconds));
    }

    public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default)
    {
        TimeSpan span = TimeSpan.Zero;
        if (options.AbsoluteExpiration != null)
        {
            span = options.AbsoluteExpiration.Value - DateTime.UtcNow;
        }
        else if (options.AbsoluteExpirationRelativeToNow != null)
        {
            span = options.AbsoluteExpirationRelativeToNow.Value;
        }
        return Task.FromResult(ExecAsync(cache => cache.Set(key, value, span == TimeSpan.Zero ? -1 : span.Seconds)));
    }

    public void Refresh(string key)
    {
        throw new NotImplementedException();
    }

    public Task RefreshAsync(string key, CancellationToken token = default)
    {
        throw new NotImplementedException();
    }

    public void Remove(string key)
    {
        Exec(cache => cache.Remove(key));
    }

    public Task RemoveAsync(string key, CancellationToken token = default)
    {
        return ExecAsync(cache => cache.Remove(key));
    }
}